/*
 *  Copyright 2005 Blandware (http://www.blandware.com)
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 */
package com.blandware.atleap.model.questionnaire;

import com.blandware.atleap.model.core.BaseObject;
import com.blandware.atleap.common.util.ConvertUtil;

import java.util.*;

/**
 * <p>Represents a questionnaire question.</p>
 * <p><a href="Question.java.html"><i>View source</i></a></p>
 *
 * @author Roman Puchkovskiy <a href="mailto:roman.puchkovskiy@blandware.com">
 *         &lt;roman.puchkovskiy@blandware.com&gt;</a>
 * @version $Revision: 1.8 $ $Date: 2007/12/24 15:53:38 $
 * @struts.form include-all="false" extends="BaseForm"
 * @hibernate.class table="`al_questionnaire_question`" lazy="false"
 * @hibernate.cache usage="read-write"
 */
public class Question extends BaseObject {

    /**
     * Single line
     */
    public static final byte LINE = 0;
    /**
     * Multi-line (textarea)
     */
    public static final byte MULTILINE = 1;
    /**
     * Drop-down list
     */
    public static final byte DROPDOWN_LIST = 2;
    /**
     * Set of radio buttons
     */
    public static final byte RADIO_BUTTONS = 3;
    /**
     * Set of checkboxes
     */
    public static final byte CHECK_BOXES = 4;
    /**
     * List where several options may be selected
     */
    public static final byte MULTILIST = 5;

	/**
	 * ID of this question
	 */
	protected Long id;

	/**
	 * Questionnaire to which this question belongs
	 */
	protected Questionnaire questionnaire;

	/**
	 * Map of titles for different locales
	 */
	protected Map title = new HashMap();

    /**
     * Map of titles used to save/load (as they are encoded)
     */
    protected Map encodedTitle = new HashMap();

    /**
     * Question type
     */
    protected byte type;

    /**
     * Whether this question is mandatory to answer
     */
    protected Boolean required;

    /**
     * Regex mask that is applied to an answer (only for text questions)
     */
    protected String validationRegex;

    /**
     * Position
     */
    protected Integer position;

    /**
     * List of answerOptions in this question
     */
    protected List answerOptions = new ArrayList();

    /**
     * List of answers to this question
     */
    protected List answers = new ArrayList();

    /**
     * Version of this question (used in optimistic locking)
     */
	protected Long version;

	/**
	 * Returns the id of the question.
	 *
	 * @return id
	 * @struts.form-field
	 * @hibernate.id column="`id`"
	 * generator-class="increment" unsaved-value="null"
	 */
	public Long getId() {
		return id;
	}

    /**
     * Sets id of the question.
     *
     * @param id the id to be set
     */
	public void setId(Long id) {
		this.id = id;
	}

	/**
	 * Returns the questionnaire.
	 *
     * @return the questionnaire
	 * @struts.form-field
	 * @hibernate.many-to-one column="`questionnaire_id`" not-null="true"
     * outer-join="false" lazy="false"
	 */
	public Questionnaire getQuestionnaire() {
		return questionnaire;
	}

    /**
     * Sets the questionnaire of this menu item.
     *
     * @param questionnaire questionnaire to set
     */
	public void setQuestionnaire(Questionnaire questionnaire) {
		this.questionnaire = questionnaire;
	}

	/**
	 * Returns the type of the question
	 *
	 * @return type
	 * @struts.form-field
	 * @struts.validator type="required"
	 * @hibernate.property column="`type`" not-null="true" unique="false"
	 */
	public byte getType() {
		return type;
	}

    /**
     * Sets the type of the question
     *
     * @param type type to set
     */
	public void setType(byte type) {
		this.type = type;
	}

    /**
     * Returns whether question is mandatory.
     *
     * @return whether question is mandatory
     * @struts.form-field
	 * @hibernate.property column="`required`" not-null="true" type="true_false"
     */
    public Boolean getRequired() {
        return required;
    }

    /**
     * Sets whether question is mandatory
     *
     * @param required whether question is mandatory
     */
    public void setRequired(Boolean required) {
        this.required = required;
    }

    /**
     * Returns validation regex.
     *
     * @return regex
     * @struts.form-field
	 * @hibernate.property column="`validation_regex`" not-null="false"
     * length="252"
     */
    public String getValidationRegex() {
        return validationRegex;
    }

    /**
     * Sets validation regex.
     *
     * @param validationRegex regex to set
     */
    public void setValidationRegex(String validationRegex) {
        this.validationRegex = validationRegex;
    }

    /**
     * Returns position.
     *
     * @return position
     * @hibernate.property column="`pos`" not-null="true" unique="false"
     */
    public Integer getPosition() {
        return position;
    }

    /**
     * Sets position.
     *
     * @param position position to set
     */
    public void setPosition(Integer position) {
        this.position = position;
    }

    /**
     * Returns a list of answer options.
     *
     * @return list of answer options
     * @hibernate.bag name="answerOptions" inverse="true" lazy="true"
     * cascade="delete" outer-join="false"
     * order-by="`pos`"
	 * @hibernate.collection-key column="`question_id`"
	 * @hibernate.collection-one-to-many class="com.blandware.atleap.model.questionnaire.AnswerOption"
     */
    public List getAnswerOptions() {
        return answerOptions;
    }

    /**
     * Sets a list of answer options.
     *
     * @param answerOptions list to set
     */
    public void setAnswerOptions(List answerOptions) {
        this.answerOptions = answerOptions;
    }

    /**
     * Adds option to this question.
     *
     * @param answerOption the option to be added
     */
	public void addAnswerOption(AnswerOption answerOption) {
		answerOption.setQuestion(this);
		answerOptions.add(answerOption);
	}

    /**
     * Updates changed option.
     *
     * @param answerOption the option to be updated
     * @return option that has been replaced
     */
	public AnswerOption updateAnswerOption(AnswerOption answerOption) {
        AnswerOption oldAnswerOption = null;
		if ( !answerOption.getQuestion().equals(this) ) {
            int index = answerOption.getQuestion().answerOptions.indexOf(answerOption);
            if (index != -1)
                oldAnswerOption = (AnswerOption) answerOption.getQuestion().answerOptions.remove(index);
			answerOptions.add(answerOption);
			answerOption.setQuestion(this);
		}
        return oldAnswerOption;
	}

    /**
     * Removes option from this question.
     *
     * @param answerOption the option to be removed
     */
	public void removeAnswerOption(AnswerOption answerOption) {
		answerOptions.remove(answerOption);
	}

    /**
     * Returns a list of answers.
     *
     * @return list of answers
     * @hibernate.bag name="answers" inverse="true" lazy="true"
     * cascade="delete" outer-join="false"
	 * @hibernate.collection-key column="`question_id`"
	 * @hibernate.collection-one-to-many class="com.blandware.atleap.model.questionnaire.Answer"
     */
    public List getAnswers() {
        return answers;
    }

    /**
     * Sets a list of answers.
     *
     * @param answers list to set
     */
    public void setAnswers(List answers) {
        this.answers = answers;
    }

    /**
     * Adds answer to this question.
     *
     * @param answer the answer to be added
     */
	public void addAnswer(Answer answer) {
		answer.setQuestion(this);
		answers.add(answer);
	}

    /**
     * Removes answer from this question.
     *
     * @param answer the answer to be removed
     */
	public void removeAnswer(Answer answer) {
		answers.remove(answer);
	}

	/**
	 * Returns version of the question.
	 *
	 * @return version
	 * @struts.form-field
	 * @hibernate.version column="`version`" type="long" unsaved-value="null"
	 */
	public Long getVersion() {
		return version;
	}

    /**
     * Sets version of the question.
     *
     * @param version the version to set
     */
	public void setVersion(Long version) {
		this.version = version;
	}

    /**
     * Returns true if question needs a textual answer.
     *
     * @return true if this is textual
     */
    public boolean isTextual() {
/*
        switch (type) {
        case LINE:
        case MULTILINE:
            return true;
        }
        return false;
*/
        return isTextual(type);
    }

    /**
     * Returns true if question of given type needs a textual answer.
     *
     * @return true if this is textual
     */
    public static boolean isTextual(byte type) {
        switch (type) {
        case LINE:
        case MULTILINE:
            return true;
        }
        return false;
    }

    /**
     * Returns true if this question needs answer options to be created.
     *
     * @return true if this is multi-option
     */
    public boolean isMultiOption() {
/*
        switch (type) {
        case DROPDOWN_LIST:
        case RADIO_BUTTONS:
        case CHECK_BOXES:
        case MULTILIST:
            return true;
        }
        return false;
*/
        return isMultiOption(type);
    }

    /**
     * Returns true if question of given type needs answer options to be
     * created.
     *
     * @param type type to check
     * @return true if this is multi-option
     */
    public static boolean isMultiOption(byte type) {
        switch (type) {
        case DROPDOWN_LIST:
        case RADIO_BUTTONS:
        case CHECK_BOXES:
        case MULTILIST:
            return true;
        }
        return false;
    }

    /**
     * Gets mapping from locale identifiers to titles of this question.
     *
     * @return mapping from locale identifiers to titles
     * @struts.form-field
     */
    public Map getTitle() {
        if (title.isEmpty()) {
            title = ConvertUtil.convertMapToStrings(getEncodedTitle());
        }
        return title;
    }

    /**
     * Sets mapping from locale identifiers to titles of this question.
     *
     * @param title mapping from locale identifiers to titles
     */
    public void setTitle(Map title) {
        this.title = title;
    }

    /**
     * Gets mapping from locale identifiers to encoded titles of this question.
     *
     * @return mapping from locale identifiers to encoded titles
     * @hibernate.map table="`al_questionnaire_question_ttl`" lazy="true"
     * cascade="all" outer-join="false"
     * @hibernate.collection-key column="`id`"
     * @hibernate.collection-index column="`locale_identifier`" type="string"
     * length="10"
     * @hibernate.collection-element column="`title`"
     * type="com.blandware.atleap.persistence.hibernate.util.BinaryBlobType"
     * not-null="false" length="${database.blob.size.max}"
     */
    public Map getEncodedTitle() {
        return encodedTitle;
    }

    /**
     * Sets mapping from locale identifiers to encoded titles of this question.
     *
     * @param title mapping from locale identifiers to encoded titles
     */
    public void setEncodedTitle(Map title) {
        this.encodedTitle = title;
    }

    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Question)) {
            return false;
        }

        final Question question = (Question) o;

        if (type != question.type) {
            return false;
        }
        if (questionnaire != null
                ? !questionnaire.equals(question.questionnaire)
                : question.questionnaire != null) {
            return false;
        }
        if (required != null
                ? !required.equals(question.required) : question.required != null) {
            return false;
        }
        if (title != null ? !title.equals(question.title) : question.title != null) {
            return false;
        }
        if (validationRegex != null
                ? !validationRegex.equals(question.validationRegex)
                : question.validationRegex != null) {
            return false;
        }

        return true;
    }

    public int hashCode() {
        int result;
        result = (questionnaire != null ? questionnaire.hashCode() : 0);
        result = 29 * result + (title != null ? title.hashCode() : 0);
        result = 29 * result + (int) type;
        result = 29 * result + (required != null ? required.hashCode() : 0);
        result = 29 * result
                + (validationRegex != null ? validationRegex.hashCode() : 0);
        return result;
    }
}

